// 
//
// Author: Christopher Schwarzer
// Date: 02/2012
//
//
#ifndef ShapeSequence_H
#define ShapeSequence_H

#include <stdint.h>
#include <vector>
#include <iostream>
#include <string>
#include <sstream>

namespace Shape
{

typedef std::vector<uint8_t>::iterator SeqIter;
typedef std::vector<uint8_t>::const_iterator ConstSeqIter;

class Node;
class Organism;

typedef int id_t;
typedef int side_t;
const side_t SIDE_NONE = -1;
const side_t SIDE_FRONT = 0;
const side_t SIDE_RIGHT = 1;
const side_t SIDE_BACK = 2;
const side_t SIDE_LEFT = 3;
const side_t SIDE_COUNT = 4;

/// A lean, serialized, tree representation of an organism shape.
/// Any Shape::Organism class can be losslessly converted to this class and back. 
/// Use this class for communication between robots and morphogenesis. 
///
/// After serialization, use the iterators with Start(), End() to iterate over the
/// serialized bytes, or access the byte vector directly:
/// uint8_t* firstbyte = &mysequence->bytes[0];
/// int length = mysequence->Size();
/// For deserialization, use one of the constructors to copy the given
/// bytes into a sequence object and use Deserialize() to attempt to parse it.
/// 
/// For morphogenesis, 
/// The root/seeding robot creates the sequence from its Shape::Organism
/// Sequence* seq = new Sequence();
/// seq->Serialize(myEggShape);
///
/// Recruited robots should receive their sequence from the recruting robot
/// Sequence* seq = new Sequence(comMessage.payloadBytes, comMessage.payloadLength);
///
/// for (side_t s = 0; s < SIDE_COUNT; i++)
/// {
///   Sequence* subtree = seq->CreateSubTree(s);
///   if (subtree == NULL)
///	    // no recruiting at this side
///   else
///   {
///		// this is the side of the recruit that should dock to this robot's side s
///     side_t childSide = subtree->SideToParent();
///		// transmit 'subtree' to the recruitee
///		// delete 'subtree' after successful recruitment
///   }
/// }
/// delete seq;
///
class Sequence
{
public:
	friend std::ostream& operator<<(std::ostream&, const Sequence&);

	Sequence() : bytes() {}
	// These constructors COPY the given bytes into a buffer that
	// is maintained by the Sequence object.
	Sequence(ConstSeqIter start, ConstSeqIter end) : bytes(start, end) {}
	Sequence(const void* buf, int size) : bytes((uint8_t*)buf, (uint8_t*)buf + size) {}
	
	// Clears the contents of the buffer before writing the new bytes.
	void Serialize(const Organism& org);
	// Returns NULL, or a partial graph on failure.
	Organism* Deserialize() const;
	
	inline ConstSeqIter Start() const	 			{ return bytes.begin(); }
	inline ConstSeqIter End() const 				{ return bytes.end(); }
	inline SeqIter Start() 				 			{ return bytes.begin(); }
	inline SeqIter End() 				 			{ return bytes.end(); }
	inline void Add(uint8_t v)						{ bytes.push_back(v); }
	inline void Clear()				     			{ bytes.clear(); }
	
	inline id_t ID() const							{ return (Size() >= 4)? bytes[2] : -1; }
	inline int Size() const 					 	{ return (int)bytes.size(); }
	
	inline int16_t GetWord(ConstSeqIter p) const  	{ return (((int16_t)*p) << 8) + *(p + 1);}
	inline void SetWord(SeqIter p, int16_t w)     	{ *p++ = w >> 8; *p = w & 0xff;}
	
	// Returns the side where the root node of this sequence is docked to the parent.
	// Returns -1 if it has no parent.
	inline side_t SideToParent() const				{ return (Size() >= 4)? (int8_t)bytes[3] : -1; }
	
	std::vector<uint8_t> bytes;
private:
	void Serialize(const Node& node);
    void Serialize(const Node& n, std::stringstream &rep);
    std::vector<std::string> splitChildren(std::string children) const;
	Node* Deserialize(std::string s) const;
};

}

#endif
